package com.ff.xyh.common.utils;

import io.jsonwebtoken.*;
import io.jsonwebtoken.impl.JwtMap;
import io.swagger.models.auth.In;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.text.SimpleDateFormat;
import java.time.format.DateTimeFormatter;
import java.util.Date;

/**
 * @author helen
 * @since 2019/10/16
 */
public class JwtUtils extends JwtMap {

    //常量
//    public static final long EXPIRE = 1000 * 60 * 60 * 24; // token过期时间 一天
//    public static final long EXPIRE = 1000 * 60 * 60; // token过期时间 一小时
//    public static final long EXPIRE = 1000 * 10; // 测试 token过期时间 10
    public static final long oneSec = 1000;
    public static final long oneMin = oneSec * 60;
    public static final long oneHour = oneMin * 60;
    public static final long oneDay = oneHour * 24;
    public static final long oneMonth = oneDay * 30;
    public static final long EXPIRE = oneMonth; // token过期时间 一个月
    public static final String SIGNINGKEY = "ukc8BDbRigUDaY6pZFfW"; // 秘钥
    public static final long UPDATE_LIMIT = oneDay; // token更新阈值 当 token 有效时间小于一天时 自动更新token;
//    public static final long UPDATE_LIMIT = 14 * oneDay - 40000; // 测试 当 token 有效时间小于10s时 自动更新token;

    /**
     * 生成jwtToken subject存放用户id
     * @param id
     * @return
     */
    public static String createJwtTokenWithUserId(String id){
        return Jwts.builder()
                .setHeaderParam("typ", "JWT")
                .setHeaderParam("alg", "HS256")
                .setSubject(id)
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRE))
                .signWith(SignatureAlgorithm.HS256, SIGNINGKEY)
                .compact();
    }

    /**
     * 生成jwtToken subject存放用户id 并指定过期时间
     * @param id
     * @param expireTimeMillis
     * @return
     */
    public static String createJwtTokenWithUserId(String id, long expireTimeMillis){
        return Jwts.builder()
                .setHeaderParam("typ", "JWT")
                .setHeaderParam("alg", "HS256")
                .setSubject(id)
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + expireTimeMillis))
                .signWith(SignatureAlgorithm.HS256, SIGNINGKEY)
                .compact();
    }
//    /**
//     * 生成token字符串
//     * @param id
//     * @param nickname
//     * @param avatar
//     * @param sex
//     * @return
//     */
//    public static String createUserJwtToken(String id, String nickname, String avatar, Integer sex){
////        System.out.println(DateTimeUtil.stampToTime(System.currentTimeMillis() + EXPIRE));
//        String JwtToken = Jwts.builder()
//                .setHeaderParam("typ", "JWT")
//                .setHeaderParam("alg", "HS256")
//
//                .setSubject("current-user")
//                .setIssuedAt(new Date())
//                .setExpiration(new Date(System.currentTimeMillis() + EXPIRE))
//
//                .claim("id", id)  //设置token主体部分 ，存储用户信息 id 昵称 头像 性别
//                .claim("nickname", nickname)
//                .claim("avatar", avatar)
//                .claim("sex", sex)
//
//                .signWith(SignatureAlgorithm.HS256, SIGNINGKEY)
//                .compact();
//
//        return JwtToken;
//    }

    /**
     * token过期自动续费方案
     * 1 token过期
     * 在我们后端生成的token是可以设置token的过期时间的例如设置了30分钟过期，则意味着30分钟过后就需要再登录一次。
     * 2 解决方案
     * 方案1: 每一次请求都进行重新生成一个新的token【频率过高，性能不好】
     * 方案2: 每次登录的时候生成两个token给前端进行返回，一个是用于鉴别用户身份的token，
     *        另外一个token则是用于刷新token用的
     * 方案3: 登录过后给前端进行返回token并设置了过期时间30分钟，每次请求的时候前端把token
     *        存在请求头里面进行发请求，后端接收请求的时候获取请求头出来进行jwt解析判断过期时间
     *        是否小于10分钟，如果小于10分钟就生成新的token在responseHeader进行返回即可
     * 这里采用方式3
     */

    /**
     * 判断token是否存在与有效
     * @param jwtToken
     * @return
     */
    public static String checkToken(String jwtToken) {
        if(StringUtils.isEmpty(jwtToken)) return "";
        Jws<Claims> claimsJws = null;
        try {
            // 秘钥与SIGNINGKEY不同 或 token过期时会抛出异常
            claimsJws =  Jwts.parser().setSigningKey(SIGNINGKEY).parseClaimsJws(jwtToken);
        } catch (Exception e) {
            e.printStackTrace();
            // 失效时返回空字符串
            return "";
        }
        // 有效时间小于 UPDATE_LIMIT时进行token更新操作
        if ( claimsJws != null ) {
            Claims claims = claimsJws.getBody();
//            boolean b = ( claims.getExpiration().getTime()
//                    -
//                    System.currentTimeMillis() ) < UPDATE_LIMIT;
//            System.out.println("有效期小于5s？" + b);
//            if ( b )
//            String timeFormat = "yyyy-MM-dd HH:mm:ss";
//            long cTimeN = claims.getExpiration().getTime();
//            long sTimeN = System.currentTimeMillis();
//            long eTimeN = EXPIRE;
//            String cTime = new SimpleDateFormat(timeFormat).format(cTimeN);
//            System.out.println("cTime ==> " + cTime);
//            String sTime = new SimpleDateFormat(timeFormat).format(sTimeN);
//            System.out.println("sTime ==> " + sTime);
//            String eTime = new SimpleDateFormat(timeFormat).format(eTimeN);
//            String nTime = new SimpleDateFormat(timeFormat).format(new Date().getTime());
//            System.out.println("eTime ==> " + eTime);
//            System.out.println("nTime ==> " + nTime);

            if ( ( claims.getExpiration().getTime()
                    -
                    System.currentTimeMillis() ) < UPDATE_LIMIT ) {
                jwtToken = updateTokenExpiration(jwtToken);
                System.out.println("token updated ==> " + jwtToken);
            }
        }
        return jwtToken;
    }
    /**
     * 判断request 中 token是否存在与有效
     * @param request
     * @return
     */
    public static String checkRequestToken(HttpServletRequest request) {
        String jwtToken = request.getHeader("token");
        return checkToken(jwtToken);
    }

    /**
     * Token续费
     * @param jwtToken
     * @return
     */
    public static String updateTokenExpiration(String jwtToken) {
//        System.out.println("update token expiration");
        if(StringUtils.isEmpty(jwtToken)) return "";
        Jws<Claims> claimsJws = null;
        try {
            claimsJws = Jwts.parser().setSigningKey(SIGNINGKEY).parseClaimsJws(jwtToken);
        } catch (Exception e) {
            return "";
        }
        if ( claimsJws!=null ) {
            Claims claims = claimsJws.getBody();
//            jwtToken = Jwts.builder()
//                    .setHeaderParam("typ", "JWT")
//                    .setHeaderParam("alg", "HS256")
//                    .setSubject("current-user")
//                    .setIssuedAt(new Date())
//                    .setExpiration(new Date(System.currentTimeMillis() + EXPIRE))
//                    .setClaims(claims) // 包含过期时间 会将上一行的过期时间设置覆盖 导致跟旧的一样
//                    .signWith(SignatureAlgorithm.HS256, SIGNINGKEY)
//                    .compact();

//            直接设置claims会报一下错
//            io.jsonwebtoken.UnsupportedJwtException: Unsigned Claims JWTs are not supported.
//            jwtToken = Jwts.builder()
//                    .setClaims(claims)
//                    .compact();

            claims.setExpiration(new Date(getExpiration(jwtToken).getTime() + EXPIRE)); // 设置过期时间
            claims.setIssuedAt(new Date()); // 设置签发时间
//            claims.setSubject(claims.getSubject()); // subject不用变
            jwtToken = Jwts.builder()
                    .setHeaderParam("typ", "JWT")
                    .setHeaderParam("alg", "HS256")
                    .setClaims(claims)
                    .signWith(SignatureAlgorithm.HS256, SIGNINGKEY)
                    .compact();

//            System.out.println(jwtToken);
//            System.out.println("update complete");

        } else
            return "";
        return jwtToken;
    }


//    /**
//     * 根据token字符串获取用户id
//     * @param jwtToken
//     * @return
//     */
//    public static String getUserIdByJwtToken(String jwtToken) {
//        if(StringUtils.isEmpty(jwtToken)) return "";
//        Jws<Claims> claimsJws = Jwts.parser().setSigningKey(SIGNINGKEY).parseClaimsJws(jwtToken);
//        Claims claims = claimsJws.getBody();
//        return (String)claims.get("id");
//    }
//    /**
//     * 根据request中的token字符串获取用户id
//     * @param request
//     * @return
//     */
//    public static String getUserIdByRequestJwtToken(HttpServletRequest request) {
//        String jwtToken = request.getHeader("token");
//        return getUserIdByJwtToken(jwtToken);
//    }

    /**
     * 获取jwt字符串过期时间
     * @param jwtToken
     * @return
     */
    public static Date getExpiration(String jwtToken) {
        if(StringUtils.isEmpty(jwtToken)) return new Date(0l);
        Jws<Claims> claimsJws = Jwts.parser().setSigningKey(SIGNINGKEY).parseClaimsJws(jwtToken);
        Claims claims = claimsJws.getBody();
        return claims.getExpiration();
    }

    /**
     * 根据token字符串获取制定数据
     * @param jwtToken token字符串
     * @param field 值域 叫何名
     * @return
     */
    public static Object getValueInJwtToken(String jwtToken, String field) {
        if(StringUtils.isEmpty(jwtToken))
            return null;
        Jws<Claims> claimsJws = null;
        try {
            claimsJws = Jwts.parser().setSigningKey(SIGNINGKEY).parseClaimsJws(jwtToken);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        if (claimsJws != null)
            return claimsJws.getBody().get(field);
        else
            return null;
    }
    /**
     * 获取subject内容 userId
     * @param token
     * @return
     */
    public static String getSubjectFromToken(String token) {
//        if (StringUtils.isEmpty(checkToken(token)))
//            return "";
        try {
            String subject = Jwts.parser().setSigningKey(SIGNINGKEY).parseClaimsJws(token).getBody().getSubject();
            return subject;
        } catch (Exception e) {
            e.printStackTrace();
            return "";
        }
    }
    
}
